package com.smap.common;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.regexp.shared.SplitResult;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;
import com.smap.core.XY;

public class Utils {
	/**
	 * map to json
	 * 
	 * @param map
	 * @return
	 */
	public static String toJson(Map<String, Object> map) {
		JSONObject jsonObj = new JSONObject();
		for (String key : map.keySet()) {
			jsonObj.put(key, new JSONString(map.get(key) + ""));
		}
		return jsonObj.toString();
	}

	public static String toJson(String[] strArray) {
		JSONArray jsonObj = new JSONArray();
		if (strArray != null)
			for (int i = 0; i < strArray.length; i++) {
				jsonObj.set(i, new JSONString(strArray[i]));
			}
		return jsonObj.toString();
	}

	/**
	 * json string to map
	 * 
	 * @param jsonStr
	 * @return
	 */
	public static Map<String, Object> toMap(String jsonStr) {
		Map<String, Object> map = new HashMap<String, Object>();
		if (jsonStr != null) {
			try {
				JSONValue jsonValue = JSONParser.parseStrict(jsonStr);
				JSONObject jsonObj = jsonValue.isObject();
				if (jsonObj != null) {
					for (String key : jsonObj.keySet()) {
						Object value = null;
						JSONValue v = jsonObj.get(key);
						if (v.isString() != null) {
							value = v.isString().stringValue();
						} else if (v.isNumber() != null) {
							value = v.isNumber().doubleValue();
						} else if (v.isBoolean() != null) {
							value = v.isBoolean().booleanValue();
						} else {
							value = v.toString();
						}
						map.put(key, value);
					}
				}
			} catch (Exception e) {
				map.put("ret", jsonStr);
			}
		}
		return map;
	}

	public static Widget clone(Widget widget) {
		final Element ele = (Element) widget.getElement().cloneNode(true);
		Widget copy = new Widget() {
			{
				setElement(ele);
			}
		};
		return copy;
	}

	public static String simpleName(Class<?> cls) {
		String simpleName = cls.getName();
		simpleName = simpleName.substring(simpleName.lastIndexOf(".") + 1);
		return simpleName;
	}

	public static Map<String, String> getAttr(JavaScriptObject jsObj) {
		Map<String, String> ret = new HashMap<String, String>();
		Node t1 = jsObj.cast();
		NodeList<Node> children = t1.getChildNodes();
		for (int i = 0; i < children.getLength(); i++) {
			Node item = children.getItem(i);
			String attributeName = item.getNodeName();
			String attributeValue = item.getNodeValue();
			ret.put(attributeName, attributeValue);
		}
		return ret;
	}

	public static void clearCanvas(Canvas mainCanvas) {
		mainCanvas.getContext2d().clearRect(0, 0, mainCanvas.getOffsetWidth(),
				mainCanvas.getOffsetHeight());
	}

	public static void setCanvasSize(Canvas mainCanvas, int width, int height) {
		mainCanvas.setWidth(width + "px");
		mainCanvas.setCoordinateSpaceWidth(width);
		mainCanvas.setHeight(height + "px");
		mainCanvas.setCoordinateSpaceHeight(height);
	}

	public static void setPosition(Widget mainCanvas, int x, int y) {
		Style st = mainCanvas.getElement().getStyle();
		st.setLeft(x, Unit.PX);
		st.setTop(y, Unit.PX);
		st.setPosition(Position.ABSOLUTE);
	}

	public static void animate(XY change, double speed,
			final AnimateCallback callback) {
		double step = 10;
		double stepX = change.x / step;
		double stepY = change.y / step;
		final LinkedList<XY> pList = new LinkedList<XY>();

		for (int i = 0; i < step; i++) {
			XY point = new XY(0, 0);
			point.x = (i * stepX);
			point.y = (i * stepY);
			pList.add(point);
		}

		final Timer t = new Timer() {
			@Override
			public void run() {
				if (pList.size() == 0) {
					this.cancel();
					if (callback != null) {
						callback.finish();
					}
				} else {
					XY p = pList.poll();
					callback.step(p.x, p.y);
				}
			}
		};
		t.scheduleRepeating((int) (30 / speed));
	}

	public static void animate(double from, double to,
			final AnimateCallback callback) {
		double step = (to - from) / 10;

		final LinkedList<Double> pList = new LinkedList<Double>();

		for (int i = 0; i < 10; i++) {
			double theValue = from + i * step;
			pList.add(theValue);
		}

		final Timer t = new Timer() {
			@Override
			public void run() {
				if (pList.size() == 0) {
					this.cancel();
					if (callback != null) {
						callback.finish();
					}
				} else {
					Double p = pList.poll();
					callback.step(p, null);
				}
			}
		};
		t.scheduleRepeating(20);
	}

	public static interface AnimateCallback {
		public void finish();

		public void step(Double x, Double y);
	}

	public static String format(final String format, final Object... args) {
		final RegExp regex = RegExp.compile("%[a-z]");
		final SplitResult split = regex.split(format);
		final StringBuffer msg = new StringBuffer();
		for (int pos = 0; pos < split.length() - 1; pos += 1) {
			msg.append(split.get(pos));
			msg.append(args[pos].toString());
		}
		msg.append(split.get(split.length() - 1));
		return msg.toString();
	}

	public static String join(Iterable<String> src, String spliter) {
		StringBuffer sb = new StringBuffer();
		for (String str : src) {
			if (sb.length() > 0) {
				sb.append(spliter);
			}
			sb.append(str);
		}
		return sb.toString();
	}

	@SuppressWarnings("rawtypes")
	public static int search(Iterable src, Object obj) {
		int index = 0;
		for (Object obj2 : src) {
			if (obj == obj2 || obj.equals(obj2)) {
				return index;
			}
			index++;
		}
		return -1;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static void moveToTop(ArrayList list, Object obj) {
		int index = search(list, obj);
		if (index > 0) {
			for (int i = 0; i < index; i++) {
				list.set(i + 1, list.get(i));
			}
		}
		list.set(0, obj);
	}

	public static void setZIndex(UIObject obj, int value) {
		Style s = obj.getElement().getStyle();
		s.setZIndex(value);
	}

	/**
	 * simple name for object class
	 * 
	 * @param obj
	 * @return
	 */

	public static String simpleName(Object obj) {
		String simpleName = obj.getClass().getName();
		return simpleName.substring(simpleName.lastIndexOf(".") + 1);
	}

	static long autoID = 0;

	synchronized public static long generateID() {
		autoID++;
		return autoID;
	}

	public static void get(String url, final HttpCallback callback) {
		// auto detect if proxy required
		
		if (url.startsWith("http") && url.contains("?")) {
			String[] strs = url.split("\\?");
			url = "./proxy?"+strs[1]+"&s="+strs[0];
		}

		RequestBuilder builder = new RequestBuilder(RequestBuilder.GET,
				URL.encode(url));
		try {
			builder.sendRequest(null, new RequestCallback() {
				public void onError(Request request, Throwable exception) {
					callback.execute(null);
				}

				public void onResponseReceived(Request request,
						Response response) {
					if (200 == response.getStatusCode()) {
						callback.execute(response.getText());
					} else {
						callback.execute(null);
					}
				}
			});
		} catch (RequestException e) {
			callback.execute(null);
		}
	}

	public static interface HttpCallback {
		public void execute(String ret);
	}

	public static String mapToUrl(Map<String, String> params) {
		StringBuffer sb = new StringBuffer();
		for (String key : params.keySet()) {
			sb.append("&" + key + "=" + params.get(key));
		}
		return sb.toString();
	}
}
